package com.compiler;

import com.annotation.BindView;
import com.annotation.MvpModel;
import com.annotation.MvpService;
import com.annotation.OnClick;
import com.compiler.model.AnnotatedClass;
import com.compiler.model.AnnotatedMvpClass;
import com.compiler.model.BindViewField;
import com.compiler.model.MvpModelType;
import com.compiler.model.MvpServiceType;
import com.compiler.model.OnClickMethod;
import com.google.auto.service.AutoService;

import java.io.IOException;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Elements;


/**
 * Created by huilin on 2017/6/10.
 */
@AutoService(Processor.class)
public class ViewInjectProcesser
        extends AbstractProcessor {

    /**
     * 文件相关的辅助类
     */
    private Filer filer;
    /**
     * 元素相关的辅助类
     */
    private Elements elements;
    /**
     * 存储注解类的映射
     */
    private Map<String, AnnotatedClass> annotatedClassMap;

    private Map<String, AnnotatedMvpClass> annotatedMvpClassMap;
    private Map<String, MvpModelType> modeleToClassMap;

    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        filer = processingEnv.getFiler();
        elements = processingEnv.getElementUtils();
        annotatedClassMap = new TreeMap<>();
        annotatedMvpClassMap = new TreeMap<>();
        modeleToClassMap = new TreeMap<>();
    }

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        annotatedClassMap.clear();
        processBindView(roundEnv);
        processOnClick(roundEnv);
        processMvpClass(roundEnv);
        for (AnnotatedClass annotatedClass : annotatedClassMap.values()) {
            try {
                annotatedClass.generateFile().writeTo(filer);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        for (AnnotatedMvpClass annotatedMvpClass:annotatedMvpClassMap.values()){
            try {
                annotatedMvpClass.generateFile().writeTo(filer);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return true;
    }

    private void processMvpClass(RoundEnvironment roundEnv) {
        Set<? extends Element> elementsAnnotatedWith = roundEnv.getElementsAnnotatedWith(MvpModel.class);
        for (Element element : elementsAnnotatedWith) {
            MvpModelType mvpModelType = new MvpModelType(element);
            AnnotatedMvpClass annotatedMvpClass = getAnnotatedMvpClass(mvpModelType.getName());
            annotatedMvpClass.addMvpModelType(mvpModelType);
            modeleToClassMap.put(mvpModelType.mvpClassName(), mvpModelType);
        }
        processMvpServiceClass(roundEnv);
    }

    private void processMvpServiceClass(RoundEnvironment roundEnv) {
        Set<? extends Element> elementsAnnotatedWith = roundEnv.getElementsAnnotatedWith
                (MvpService.class);
        for (Element element : elementsAnnotatedWith) {
            MvpServiceType mvpServiceType = new MvpServiceType(element);
            if (modeleToClassMap.containsKey(mvpServiceType.mvpClassName())) {
                MvpModelType mvpModelType = modeleToClassMap.get(mvpServiceType.mvpClassName());
                mvpModelType.mvpServiceType = mvpServiceType;
            }
        }
    }

    private void processBindView(RoundEnvironment roundEnv) {
        for (Element element : roundEnv.getElementsAnnotatedWith(BindView.class)) {
            AnnotatedClass annotatedClass = getAnnotatedClass(element);
            annotatedClass.addField(new BindViewField(element));
        }
    }

    private void processOnClick(RoundEnvironment roundEnvironment) {
        for (Element element : roundEnvironment.getElementsAnnotatedWith(OnClick.class)) {
            AnnotatedClass annotatedClass = getAnnotatedClass(element);
            annotatedClass.addMethod(new OnClickMethod(element));
        }
    }

    /**
     * 指定使用的java版本 通常返回 SourceVersion.latestSupported()。
     *
     * @return
     */
    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported();
    }

    /**
     * 指定哪些注解应该被注解处理器注册
     *
     * @return
     */
    @Override
    public Set<String> getSupportedAnnotationTypes() {
        Set<String> stringSet = new LinkedHashSet<>();
        //        stringSet.add(BindView.class.getCanonicalName());
        //        stringSet.add(OnClick.class.getCanonicalName());
        stringSet.add(MvpService.class.getCanonicalName());
        stringSet.add(MvpModel.class.getCanonicalName());
        return stringSet;
    }


    public AnnotatedClass getAnnotatedClass(Element element) {
        TypeElement typeElement = (TypeElement) element.getEnclosingElement();
        String fullName = typeElement.getQualifiedName().toString();
        AnnotatedClass annotatedClass = annotatedClassMap.get(fullName);
        if (annotatedClass == null) {
            annotatedClass = new AnnotatedClass(typeElement, elements);
            annotatedClassMap.put(fullName, annotatedClass);
        }
        return annotatedClass;
    }

    public AnnotatedMvpClass getAnnotatedMvpClass(String name) {
        AnnotatedMvpClass annotatedMvpClass = annotatedMvpClassMap.get(name);
        if (annotatedMvpClass == null) {
            annotatedMvpClass = new AnnotatedMvpClass(elements);
            annotatedMvpClassMap.put(name, annotatedMvpClass);
        }
        return annotatedMvpClass;
    }
}
